// TortoiseSVN - a Windows shell extension for easy version control

// Copyright (C) 2008-2010, 2011-2013 - TortoiseSVN

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
#pragma once

#include "SVN.h"
#include "SVNPrompt.h"
#include "SVNLogQuery.h"
#include "CacheLogQuery.h"
#include "JobScheduler.h"

class CFullGraphNode;

/**
 * \ingroup TortoiseProc
 * helper struct containing information about a copy operation in the revision graph
 */
class SCopyInfo
{
public:

    revision_t fromRevision;
    index_t fromPathIndex;
    revision_t toRevision;
    index_t toPathIndex;

    struct STarget
    {
        CFullGraphNode* source;
        CDictionaryBasedTempPath path;

        STarget ( CFullGraphNode* source
                , const CDictionaryBasedTempPath& path)
            : source (source)
            , path (path)
        {
        }
    };

    std::vector<STarget> targets;

    static SCopyInfo* Create (boost::pool<>& copyInfoPool)
    {
        SCopyInfo* result = static_cast<SCopyInfo*>(copyInfoPool.malloc());
        new (result) SCopyInfo();
        return result;
    }

    void Destroy (boost::pool<>& copyInfoPool)
    {
        this->~SCopyInfo();
        copyInfoPool.free (this);
    }
};

/**
 * \ingroup TortoiseProc
 * Handles and analyzes log data to produce a revision graph.
 *
 * Since Subversion only stores information where each entry is copied \b from
 * and not where it is copied \b to, the first thing we do here is crawl all
 * revisions and create separate CRevisionEntry objects where we store the
 * information where those are copied \b to.
 *
 * In a next step, we go again through all the CRevisionEntry objects to find
 * out if they are related to the path we're looking at. If they are, we mark
 * them as \b in-use.
 */

class CFullHistory : private ILogReceiver
{
public:

    /**
     * \ingroup TortoiseProc
     * Contains all WC status relevant for the revision graph.
     */

    struct SWCInfo
    {
        revision_t minAtRev;
        revision_t maxAtRev;
        revision_t minCommit;
        revision_t maxCommit;

        bool modified;

        SWCInfo (revision_t rev = -1)
            : minAtRev (rev)
            , maxAtRev (rev)
            , minCommit (rev)
            , maxCommit (rev)
            , modified (false)
        {
        }
    };

    /// construction / destruction

    CFullHistory(void);
    ~CFullHistory(void);

    /// query data

    bool                        FetchRevisionData ( CString path
                                                  , SVNRev pegRev
                                                  , bool showWCRev
                                                  , bool showWCModification
                                                  , CProgressDlg* progress
                                                  , ITaskbarList3 * pTaskBarList
                                                  , HWND hWnd);

    /// data access

    CString                     GetLastErrorMessage() const;

    svn_revnum_t                GetHeadRevision() const {return headRevision;}
    svn_revnum_t                GetPegRevision() const {return pegRevision;}
    CString                     GetRepositoryRoot() const {return CString (repoRoot);}
    CString                     GetRepositoryUUID() const {return uuid;}
    CString                     GetRelativePath() const {return CString (relPath);}

    const CDictionaryBasedTempPath* GetStartPath() const {return startPath.get();}
    revision_t                  GetStartRevision() const {return startRevision;}

    const CDictionaryBasedTempPath* GetWCPath() const {return wcPath.get();}
    const SWCInfo&              GetWCInfo() const {return wcInfo;}

    SCopyInfo**                 GetFirstCopyFrom() const {return copyFromRelation;}
    SCopyInfo**                 GetFirstCopyTo() const {return copyToRelation;}
    void                        GetCopyFromRange (SCopyInfo**& first, SCopyInfo**& last, revision_t revision) const;
    void                        GetCopyToRange (SCopyInfo**& first, SCopyInfo**& last, revision_t revision) const;

    SVN&                        GetSVN() {return svn;}
    const CCachedLogInfo*       GetCache() const {return cache;}

private:

    /// data members

    CProgressDlg*               progress;
    ITaskbarList3*              taskbarlist;
    HWND                        hwnd;

    CStringA                    repoRoot;
    CStringA                    relPath;
    CString                     uuid;
    revision_t                  headRevision;
    revision_t                  pegRevision;
    revision_t                  firstRevision;

    svn_client_ctx_t *          ctx;
    SVNPrompt                   prompt;
    SVN                         svn;

    svn_error_t *               Err;            ///< Global error object struct
    apr_pool_t *                parentpool;
    apr_pool_t *                pool;           ///< memory pool

    bool                        cancelled;

    CCachedLogInfo*             cache;
    std::unique_ptr<CSVNLogQuery> svnQuery;
    std::unique_ptr<CCacheLogQuery> query;

    std::unique_ptr<CDictionaryBasedTempPath> startPath;
    revision_t                  startRevision;

    std::unique_ptr<CDictionaryBasedTempPath> wcPath;
    SWCInfo                     wcInfo;

    boost::pool<>               copyInfoPool;
    std::vector<SCopyInfo*>     copiesContainer;
    SCopyInfo**                 copyToRelation;
    SCopyInfo**                 copyToRelationEnd;
    SCopyInfo**                 copyFromRelation;
    SCopyInfo**                 copyFromRelationEnd;

    /// asynchronuous execution queues
    /// (one per independent resource)

    async::CJobScheduler        diskIOScheduler;
    async::CJobScheduler        cpuLoadScheduler;

    /// SVN callback

    static svn_error_t*         cancel(void *baton);

    /// utility methods

    bool                        ClearCopyInfo();
    void                        QueryWCRevision (bool doQuery, CString path);
    void                        AnalyzeRevisionData();
    void                        BuildForwardCopies();

    /// implement ILogReceiver

    void ReceiveLog ( TChangedPaths* changes
                    , svn_revnum_t rev
                    , const StandardRevProps* stdRevProps
                    , UserRevPropArray* userRevProps
                    , const MergeInfo* mergeInfo) override;

};
